<template>
  <div>
    <!-- 如果文件比较大的话，主线程会比较卡，可以尝试开启多线程使用。 -->
    <h1>上传文件</h1>
    <input type="file" @change="handleFileChange" :disabled="isUploading" />
    <input type="file" @change="fileChange" />

    <div v-if="isUploading" class="upload-progress">
      <div class="progress-bar">
        <div
          class="progress-fill"
          :style="{ width: uploadProgress + '%' }"
        ></div>
      </div>
      <p>上传进度: {{ uploadProgress }}%</p>
    </div>
  </div>
</template>


<script>
import SparkMD5 from "spark-md5";
export default {
  data() {
    return {
      chunkSize: 2 * 1024 * 1024, // 2MB per chunk
      uploadProgress: 0,
      isUploading: false,
    };
  },
  methods: {
    fileChange(e) {
      console.log("fileChange:", e);
      const file = e.target.files[0];
      console.log("file:", file);
      this.createChunks(file, this.chunkSize);
    },
    // 分片上传
    createChunks(file, chunkSize) {
      const result = [];
      for (let i = 0; i < file.size; i += chunkSize) {
        const chunk = file.slice(i, i + chunkSize);
        result.push(chunk);
      }
      console.log("result:", result);
      return result;
    },

    async handleFileChange(e) {
      console.log("upload:", e);
      const file = e.target.files[0];
      if (!file) return;
      console.log(file);
      this.isUploading = true;
      this.uploadProgress = 0;
      try {
        await this.uploadFileInChunks(file);
        console.log("文件上传完成");
      } catch (error) {
        console.error("上传失败:", error);
      } finally {
        this.isUploading = false;
      }
    },

    async uploadFileInChunks(file) {
      const fileSize = file.size;
      // 计算文件总块数
      const totalChunks = Math.ceil(fileSize / this.chunkSize);
      // 计算文件哈希值
      const fileHash = await this.calculateFileHash(file);

      // 检查文件是否已存在（秒传功能）
      const existsResponse = await this.checkFileExists(fileHash, file.name);
      if (existsResponse.exists) {
        console.log("文件已存在，秒传成功");
        return;
      }

      // 分片上传
      for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
        const start = chunkIndex * this.chunkSize;
        const end = Math.min(start + this.chunkSize, fileSize);
        const chunk = file.slice(start, end);

        await this.uploadChunk(
          chunk,
          chunkIndex,
          totalChunks,
          fileHash,
          file.name
        );

        // 更新进度
        this.uploadProgress = Math.round(
          ((chunkIndex + 1) / totalChunks) * 100
        );
      }

      // 通知服务端合并分片
      await this.mergeChunks(fileHash, file.name, totalChunks);
    },

    // 替换 calculateFileHash 方法
    async calculateFileHash(file) {
      return new Promise((resolve, reject) => {
        const chunkSize = 2 * 1024 * 1024; // 2MB
        const chunks = Math.ceil(file.size / chunkSize);
        let currentChunk = 0;
        const spark = new SparkMD5.ArrayBuffer();
        const fileReader = new FileReader();

        fileReader.onload = (e) => {
          spark.append(e.target.result);
          currentChunk++;

          if (currentChunk < chunks) {
            loadNextChunk();
          } else {
            const hash = spark.end();
            resolve(hash);
          }
        };

        fileReader.onerror = () => reject(new Error("文件读取失败"));

        const loadNextChunk = () => {
          const start = currentChunk * chunkSize;
          const end = Math.min(start + chunkSize, file.size);
          const chunk = file.slice(start, end);
          fileReader.readAsArrayBuffer(chunk);
        };

        loadNextChunk();
      });
    },

    async checkFileExists(fileHash, fileName) {
      try {
        const response = await fetch("/api/check-file", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            fileHash,
            fileName,
          }),
        });
        return await response.json();
      } catch (error) {
        console.error("检查文件存在性失败:", error);
        return { exists: false };
      }
    },

    async uploadChunk(chunk, chunkIndex, totalChunks, fileHash, fileName) {
      const formData = new FormData();
      formData.append("chunk", chunk);
      formData.append("chunkIndex", chunkIndex.toString());
      formData.append("totalChunks", totalChunks.toString());
      formData.append("fileHash", fileHash);
      formData.append("fileName", fileName);

      const response = await fetch("/api/upload-chunk", {
        method: "POST",
        body: formData,
      });

      if (!response.ok) {
        throw new Error(`分片${chunkIndex}上传失败`);
      }

      return await response.json();
    },

    async mergeChunks(fileHash, fileName, totalChunks) {
      const response = await fetch("/api/merge-chunks", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          fileHash,
          fileName,
          totalChunks,
        }),
      });

      if (!response.ok) {
        throw new Error("合并分片失败");
      }

      return await response.json();
    },
  },
};
</script>

<style scoped>
.upload-progress {
  margin-top: 20px;
}

.progress-bar {
  width: 100%;
  height: 20px;
  background-color: #f0f0f0;
  border-radius: 10px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background-color: #409eff;
  transition: width 0.3s ease;
}
</style>
